/*
 *  NFCSigning - Open source library for signing/validation of NDEF messages
 *  Copyright (C) 2009-2010 The NFCSigning Team
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 *
 */
/*
 * CertificatesTestCase.java
 * JMUnit based test
 *
 * Created on 2008-sep-26, 16:07:47
 */

package org.nfcsigning.certificate;

import org.nfcsigning.bc.crypto.signers.PKCS1Signer;
import org.nfcsigning.algorithm.DSASatsaVerifier;
import org.nfcsigning.algorithm.ImplementationDetector;
import org.nfcsigning.algorithm.PrivateKey;
import org.nfcsigning.algorithm.PublicKey;
import org.nfcsigning.algorithm.RsaSsaPkcs1V15SatsaVerifier;
import org.nfcsigning.algorithm.RsaSsaPkcs1v15BcVerifier;
import org.nfcsigning.algorithm.SignatureException;
import org.nfcsigning.algorithm.SignatureVerifier;
import org.nfcsigning.record.CertificateBytes;
import java.io.IOException;
import java.io.InputStream;
import java.util.Calendar;
import jmunit.framework.cldc11.AssertionFailedException;
import jmunit.framework.cldc11.TestCase;
import org.bouncycastle.asn1.pkcs.RSAPrivateKeyStructure;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.digests.SHA1Digest;
import org.bouncycastle.crypto.engines.RSAEngine;
import org.bouncycastle.crypto.params.RSAKeyParameters;

/**
 * @author emarkki
 */
public class CertificatesTestCase extends TestCase {
    
    private ImplementationDetector implDetector = ImplementationDetector.getInstance();
    private Files files = new Files();
    
    public CertificatesTestCase() {
        // The first parameter of inherited constructor is the number of test cases
        super(22, "CertificateTestCase");
    }            

    public void test(int testNumber) throws Throwable {

        System.out.println("\n\n------ T E S T  " + testNumber + "  -------");
        
        switch (testNumber) {
            case 0:
                test1();
                break;
            case 1:
                testX509Certificates();
                break;
            case 2:
//                testTrainCompanyCAFromInputStream();
                break;
            case 3:
                testValidateChain();
                break;
            case 4:
                testChainOrdered();
                break;
            case 5:
                testWrongRootCertificate();
                break;
            case 6:
                testWrongCertificateInChain();
                break;
            case 7:
                testToDate();
                break;
            case 8:
                testValidDate();
                break;
            case 9:
                testValidateChainCont();
                break;
            case 10:
                testRsaSsaPkcs1V15SatsaVerifier();
                break;
            case 11:
                testRsaSsaPkcs1v15BcVerifier();
                break;
            case 12:
                testPKCS1Signer();
                break;
            case 13:
                testDSASatsaVerifier();
                break;
            case 14:
                assertTrue("SATSA available", ImplementationDetector.getInstance().isSatsaAvailable());
                break;
            case 15:
                testFindMatchingRootCertificate();
                break;
            case 16:
                testValidateChainNoKeyUsageSign();
                break;
            case 17:
                testValidateChainNoCA();
                break;
            case 18:
                testValidateChainCriticalExtension();
                break;
            case 19:
                testValidateChainPathLen();
                break;
            case 20:
                testValidateChainDSAParamsInherited();
                break;
            case 21:
                testValidateCertWithRSASSAPSS();
                break;
            case 22:
                
                break;
            case 23:
                
                break;
            case 24:
                
                break;
            case 25:
                
                break;
            default:
                break;
        }
    }
   
    /**
     * Dummy test
     */
    public void test1() throws AssertionFailedException {
        System.out.println("test1");
        
        System.out.println("SATSA available: " + ImplementationDetector.getInstance().isSatsaAvailable());
        
//        Certificate cert = new Certificate(new byte[]{});
//        byte[] b = cert.getBytes();
    }

    public void testX509Certificates() throws AssertionFailedException {
        System.out.println("testX509Certificates");
        
        testMarkus1PubFromInputStream("markus1");
        testTrainCompanyCAFromInputStream("trainCA");
        testMoraNisseFromInputStream("moraNisse");
        testLarsNilsFromInputStream("larsNils");
        testScandinaviaRootCAFromInputStream("scandRoot");
    }
    
    public void testMarkus1PubFromInputStream(String title) throws AssertionFailedException {
        try {
            System.out.println(title);
            
            X509Certificate x509 = new X509Certificate(getClass().getResourceAsStream("files/markus1-pub.cer"));
            
            assertEquals(title+": subject", "C=SE,O=Org AB,OU=OrgUnit,CN=Markus cert2", x509.getStructure().getSubject().toString());
            assertEquals(title+": issuer", "C=SE,O=TrainCompany AB,OU=Certification Authority,CN=TrainCompany CA", x509.getStructure().getIssuer().toString());
            assertEquals(title+": Start date", "20080917083430GMT+00:00", x509.getStructure().getStartDate().getTime());
            assertEquals(title+": End date", "20090917083431GMT+00:00", x509.getStructure().getEndDate().getTime());
            assertEquals(title+": Signature", "0b562b4b5436ccf6023b9918b027f217ba891462d55e114a26127699a666604d8f996f582815b235d9bebf6422e55a29255464966766b8305a864b41f2043209f81f63712925d86d0c35a007af2648b57298660f0faa0abf59cbb9f72a685a03c70120b2b4c53ec28798de7ad2f7de3f00f4582557a0d28e5063d6c421817b7e13d621b4e63883cd3e25b9dcb6f89c356cbe350640b1ccd2ba7b49088b31508ded56665cf4cc1d1a0c2728e33e84edba459564f73f228739b4a7e39b1b9b9327e8846c4c1be5b7c51084074a0555c8c06935caf7c4c08ca0a9cbfd588689dac8cb7b77ea96839ca5bbf31cc191b3744e539e89c22c19d455c4b4d4e208b61d41",
                    Utils.toHexString(x509.getStructure().getSignature().getBytes()));
            assertEquals(title+": version", 3, x509.getStructure().getVersion());
        } catch (IOException ex) {
            ex.printStackTrace();
            fail(title+": IOException: "+ex.getMessage());
        }
    }
    
    private void testTrainCompanyCAFromInputStream(String title) throws AssertionFailedException {
        try {
            System.out.println(title);
            
            X509Certificate x509 = new X509Certificate(getClass().getResourceAsStream("files/Other-TrainCompany-CA.cer"));
            
            assertEquals(title+": subject", "C=SE,O=TrainCompany AB,OU=Certification Authority,CN=TrainCompany CA", x509.getStructure().getSubject().toString());
            assertEquals(title+": issuer", "C=SE,O=TrainCompany AB,OU=Certification Authority,CN=TrainCompany CA", x509.getStructure().getIssuer().toString());
            assertEquals(title+": Start date", "20080917065216GMT+00:00", x509.getStructure().getStartDate().getTime());
            assertEquals(title+": End date", "20180918065229GMT+00:00", x509.getStructure().getEndDate().getTime());
            assertEquals(title+": Signature", "6c9f4e10212eaaa655fff6cf8a029f6f99367769a9a98c39445a1cd5e9c6d226a1b2adf31aad222c1e6a4ce7f1bc328529de211db6cb124a8b661b0c3959a85963a3fccf4ecaccfaf066a2d8990df7aaf5ca63dece8136cb25de96514285fb653d583e5ecc0bd41a82bad50edc23755ac584bce21f032d76846e56697e06ab22e4d1ed344f3ac4b60fe7504eec66cd7104aee17f6ef305063aece3b0ff490d828ce36bfe80a302ddeaac6454de42d533438c7c6611fc552d0dff87a9fb53f460790cf9a1d357dee73d6b669be82e20b6a07e05928f9ac52539dce8d21ccad6c4723377a2a3f6ac0bfcdcb627742a05db2706ea9ddf6f9e5b1d481f800de0796f",
                    Utils.toHexString(x509.getStructure().getSignature().getBytes()));
            assertEquals(title+": version", 3, x509.getStructure().getVersion());
        } catch (IOException ex) {
            ex.printStackTrace();
            fail(title+": IOException: "+ex.getMessage());
        }
    }
    
    private void testMoraNisseFromInputStream(String title) throws AssertionFailedException {
        try {
            System.out.println(title);
            
            InputStream in = getClass().getResourceAsStream(Files.MORA_NISSE);
            
            assertNotNull(title+": not null", in);
            
            X509Certificate x509 = new X509Certificate(in);
            
            assertEquals(title+": subject", "C=SE,CN=Mora-Nisse,E=mora.nisse@dalarna.se", x509.getStructure().getSubject().toString());
            assertEquals(title+": issuer", "C=SE,O=Dalarna CA,CN=Dalarna CA", x509.getStructure().getIssuer().toString());
            assertEquals(title+": Start date", "20081029124334GMT+00:00", x509.getStructure().getStartDate().getTime());
            assertEquals(title+": End date", "20101203124334GMT+00:00", x509.getStructure().getEndDate().getTime());
            assertEquals(title+": Signature", "303502190094e135b6b78470a2d6eb728551b031122d4d01d09e3c75f20218114e0a618b0468ddbcb44743940e8c1678c80768ec3532a2",
                    Utils.toHexString(x509.getStructure().getSignature().getBytes()));
            assertEquals(title+": version", 1, x509.getStructure().getVersion());
        } catch (IOException ex) {
            ex.printStackTrace();
            fail(title+": IOException: "+ex.getMessage());
        }
    }
    
    private void testLarsNilsFromInputStream(String title) throws AssertionFailedException {
        try {
            System.out.println(title);
            
            InputStream in = getClass().getResourceAsStream(Files.LARS_NILS);
            
            assertNotNull(title+": not null", in);
            
            X509Certificate x509 = new X509Certificate(in);
            
            assertEquals(title+": subject", "C=SE,L=Leksand,O=Dalatrafik AB,CN=Lars-Nils", x509.getStructure().getSubject().toString());
            assertEquals(title+": issuer", "C=SE,ST=Some-State,L=Dalarna,O=Dalatrafik AB,CN=Dalatrafik CA,E=info@dalatrafik.se", x509.getStructure().getIssuer().toString());
            assertEquals(title+": Start date", "20081104131218GMT+00:00", x509.getStructure().getStartDate().getTime());
            assertEquals(title+": End date", "20101209131218GMT+00:00", x509.getStructure().getEndDate().getTime());
            assertEquals(title+": Signature", "302c02142574641e870400127936b9fdb7f35089bf50818302143f0bc21866ccec62603079ec71c01d690520ce0d",
                    Utils.toHexString(x509.getStructure().getSignature().getBytes()));
            assertEquals(title+": version", 1, x509.getStructure().getVersion());
        } catch (IOException ex) {
            ex.printStackTrace();
            fail(title+": IOException: "+ex.getMessage());
        }
    }
    
    private void testScandinaviaRootCAFromInputStream(String title) throws AssertionFailedException {
        try {
            System.out.println(title);
            
            InputStream in = getClass().getResourceAsStream(Files.SCANDINAVIA_ROOT_CA);
            
            assertNotNull(title+": not null", in);
            
            X509Certificate x509 = new X509Certificate(in);
            
//            System.out.println(x509.getStructure().getSubject());
//            System.out.println(x509.getStructure().getIssuer());
//            System.out.println(x509.getStructure().getStartDate().getTime());
//            System.out.println(x509.getStructure().getEndDate().getTime());
//            System.out.println(Utils.toHexString(x509.getStructure().getSignature().getBytes()));
            
            assertEquals(title+": subject", "C=SE,ST=Scandinavia,O=Scandinavian CA Ltd,CN=Scandinavia Root CA", x509.getStructure().getSubject().toString());
            assertEquals(title+": issuer", "C=SE,ST=Scandinavia,O=Scandinavian CA Ltd,CN=Scandinavia Root CA", x509.getStructure().getIssuer().toString());
            assertEquals(title+": Start date", "20081105153321GMT+00:00", x509.getStructure().getStartDate().getTime());
            assertEquals(title+": End date", "20360323153321GMT+00:00", x509.getStructure().getEndDate().getTime());
            assertEquals(title+": Signature", "302c02140d99a4840e1d7ef329bac93dfe5dbef07fdc99e202141a8beaeab0eae100597ba43e708a707020b0baa4",
                    Utils.toHexString(x509.getStructure().getSignature().getBytes()));
            assertEquals(title+": version", 3, x509.getStructure().getVersion());
        } catch (IOException ex) {
            ex.printStackTrace();
            fail(title+": IOException: "+ex.getMessage());
        }
    }
    
    private void testValidateChainCont() throws AssertionFailedException {
                
        try {
            System.out.println("testValidateChainCont");
            
            byte[] s;
            
            // Chain: Adam Adamsson
            X509Certificate cert = new X509Certificate(getClass().getResourceAsStream("files/KalleKarlsson.cer"));
            PublicKey pubKey = new PublicKey(cert.getSubjectPublicKeyInfo());
            PrivateKey privKey = new PrivateKey(getClass().getResourceAsStream("files/KalleKarlsson-priv.der"));
            
            byte[] plainText = cert.getStructure().getTBSCertificate().getEncoded();

            RSAPrivateKeyStructure structure = RSAPrivateKeyStructure.getInstance(privKey.getPrivateKeyInfo().getPrivateKey());
            RSAKeyParameters pubParameters = new RSAKeyParameters(true, structure.getModulus(), structure.getPrivateExponent());
            
            PKCS1Signer signer = new PKCS1Signer(new RSAEngine(), new SHA1Digest());
            signer.init(true, pubParameters);
            signer.update(plainText, 0, plainText.length);
            
            s = signer.generateSignature();

            if(!verifyUsingBoth(s, plainText, pubKey)) {
                fail("verify 1 failed");
            }
            
            X509Certificate root = new X509Certificate(getClass().getResourceAsStream("files/SwedenRootCA.cer"));
            
            System.out.println("Sweden self signed");
            if(!verifyUsingBoth(root.getStructure().getSignature().getBytes(), root.getStructure().getTBSCertificate().getEncoded(), new PublicKey(root.getSubjectPublicKeyInfo()))) {
                fail("verify 2 failed");
            }
            
        } catch (CryptoException ex) {
            ex.printStackTrace();
        } catch (DataLengthException ex) {
            ex.printStackTrace();
        } catch (IOException ex) {
            ex.printStackTrace();
        }        
    }
    
    private boolean verifyUsingBoth(byte[] s, byte[] plainText, PublicKey pubKey) throws AssertionFailedException {
        boolean failed = false;
            {
                RsaSsaPkcs1v15BcVerifier verifier = RsaSsaPkcs1v15BcVerifier.getInstance();
                try {
                    if (!verifier.verifySignature(s, plainText, pubKey)) {
                        System.out.println("BC: Bad signature on certificate ");
                        failed = true;
                    } else {
                        System.out.println("OK with RsaSsaPkcs1V15Verifier");
                    }
                } catch (SignatureException ex) {
                    ex.printStackTrace();
                }
            }
        
        
            if(implDetector.isSatsaAvailable()) {
            
                RsaSsaPkcs1V15SatsaVerifier verifier = RsaSsaPkcs1V15SatsaVerifier.getInstance();
                try {
                    if (!verifier.verifySignature(s, plainText, pubKey)) {
                        System.out.println("SATSA: Bad signature on certificate ");
                        failed = true;
                    } else {
                        System.out.println("OK with RsaSsaPkcs1V15SatsaVerifier");
                    }
                } catch (SignatureException ex) {
                    ex.printStackTrace();
                }
            } else {
                System.out.println("Not testing with SATSA");
            }
            return !failed;
    }
    
    private void testValidateChain() throws AssertionFailedException {
        System.out.println("testValidateChain");
        
        // Chain: markus1
        try {
            CertificateBytes[] chainBytes = new CertificateBytes[]{new CertificateBytes(files.getBytes(Files.MARKUS1))};
            Certificate[] trustedCerts = new Certificate[] { new X509Certificate(getClass().getResourceAsStream("files/Other-TrainCompany-CA.cer")) };
        
            X509CertificateChain certChain = new X509CertificateChain(chainBytes);
            
            if(!certChain.validateChain(trustedCerts)) {
                fail("markus1: " + certChain.getValidationMessage());
            }
            
        } catch (InvalidCertificateChain ex) {
            ex.printStackTrace();
            fail("markus1: "+ex.getMessage());
        } catch (IOException ex) {
            ex.printStackTrace();
            fail("markus1: "+ex.getMessage());
        }
        
        // Chain: Adam Adamsson
        try {
            X509Certificate bus = new X509Certificate(getClass().getResourceAsStream("files/BusCompanyCA.cer"));
            byte[] busHacked = bus.getBytes();
        
//            System.out.println("busHacked: " + busHacked[280]);
            busHacked[280] = 106;
            X509Certificate hacked = new X509Certificate(busHacked);
        
            X509Certificate[] chain = new X509Certificate[] {
                new X509Certificate(getClass().getResourceAsStream("files/AdamAdamsson.cer")),
                hacked,
                new X509Certificate(getClass().getResourceAsStream("files/StockholmslanCA.cer")),
                new X509Certificate(getClass().getResourceAsStream("files/SodermanlandCA.cer"))
            };
            X509Certificate[] trustedCerts = new X509Certificate[]{new X509Certificate(getClass().getResourceAsStream("files/SwedenRootCA.cer"))};

            CertificateChain certChain = new X509CertificateChain(chain);
            
            boolean consistent = certChain.validateChain(trustedCerts);
            
            assertFalse("Adam incosistent", consistent);
            
        } catch (InvalidCertificateChain ex) {
            // OK
        } catch (IOException ex) {
            ex.printStackTrace();
            fail("Adam: "+ex.getMessage());
        }
        
        // Chain: David Davidsson
        try {
            
            X509Certificate[] chain = new X509Certificate[] {
                new X509Certificate(getClass().getResourceAsStream("files/DavidDavidsson.cer")),
                new X509Certificate(getClass().getResourceAsStream("files/TrainCompanyCA.cer")),
                new X509Certificate(getClass().getResourceAsStream("files/StockholmslanCA.cer")),
                new X509Certificate(getClass().getResourceAsStream("files/SodermanlandCA.cer"))
            };
            Certificate[] trustedCerts = new X509Certificate[]{new X509Certificate(getClass().getResourceAsStream("files/SwedenRootCA.cer"))};
            CertificateChain certChain = new X509CertificateChain(chain);
            
            assertTrue("David", certChain.validateChain(trustedCerts));
            
        } catch (InvalidCertificateChain ex) {
            ex.printStackTrace();
            fail("David: "+ex.getMessage());
        } catch (IOException ex) {
            ex.printStackTrace();
            fail("David: "+ex.getMessage());
        }
        
        // Chain: Sigurd Sigurdsson
        try {
            
            X509Certificate[] chain = new X509Certificate[] {
                new X509Certificate(getClass().getResourceAsStream("files/SigurdSigurdsson.cer")),
                new X509Certificate(getClass().getResourceAsStream("files/StockholmslanCA.cer")),
                new X509Certificate(getClass().getResourceAsStream("files/SodermanlandCA.cer"))
            };
            Certificate[] trustedCerts = new X509Certificate[]{new X509Certificate(getClass().getResourceAsStream("files/SwedenRootCA.cer"))};
            CertificateChain certChain = new X509CertificateChain(chain);
            
            assertTrue("Sigurd", certChain.validateChain(trustedCerts));
            
        } catch (InvalidCertificateChain ex) {
            ex.printStackTrace();
            fail("Sigurd: "+ex.getMessage());
        } catch (IOException ex) {
            ex.printStackTrace();
            fail("Sigurd: "+ex.getMessage());
        }
        
        // Chain: Mora-Nisse
        try {
            
            X509Certificate[] chain = new X509Certificate[] {
                new X509Certificate(getClass().getResourceAsStream(Files.MORA_NISSE)),
                new X509Certificate(getClass().getResourceAsStream(Files.DALARNA_CA)),
            };
            Certificate[] trustedCerts = new X509Certificate[]{new X509Certificate(getClass().getResourceAsStream(Files.NORDIC_ROOT_CA))};//"files/DalarnaRootCA.cer"));
            CertificateChain certChain = new X509CertificateChain(chain);
            
            assertTrue("Mora-Nisse", certChain.validateChain(trustedCerts));
            
        } catch (InvalidCertificateChain ex) {
            ex.printStackTrace();
            fail("Mora-Nisse: "+ex.getMessage());
        } catch (IOException ex) {
            ex.printStackTrace();
            fail("Mora-Nisse: "+ex.getMessage());
        }
        
        // Chain: Mora-Nisse (inconsistent)
        try {
            X509Certificate nisse = new X509Certificate(getClass().getResourceAsStream(Files.MORA_NISSE));
            byte[] nisseHacked = nisse.getBytes();
//            System.out.println("busHacked: " + busHacked[280]);
            nisseHacked[280] = 106;
            X509Certificate hacked = new X509Certificate(nisseHacked);
            
            X509Certificate[] chain = new X509Certificate[] {
                hacked,
                new X509Certificate(getClass().getResourceAsStream(Files.DALARNA_CA)),
            };
            Certificate[] trustedCerts = new X509Certificate[]{new X509Certificate(getClass().getResourceAsStream(Files.NORDIC_ROOT_CA))};//"files/DalarnaRootCA.cer"));
            CertificateChain certChain = new X509CertificateChain(chain);
            
            assertFalse("Mora-Nisse2", certChain.validateChain(trustedCerts));
            
        } catch (InvalidCertificateChain ex) {
            // OK
        } catch (IOException ex) {
            ex.printStackTrace();
            fail("Mora-Nisse2: "+ex.getMessage());
        }
        
        // Chain: Norrland CA
        try {
            
            X509Certificate[] chain = new X509Certificate[] {
                new X509Certificate(getClass().getResourceAsStream(Files.NORRLAND_CA)),
            };
            Certificate[] trustedCerts = new X509Certificate[]{new X509Certificate(getClass().getResourceAsStream(Files.SCANDINAVIA_ROOT_CA))};
            CertificateChain certChain = new X509CertificateChain(chain);
            
            assertTrue("norrland", certChain.validateChain(trustedCerts));
            
        } catch (InvalidCertificateChain ex) {
            ex.printStackTrace();
            fail("norrland: "+ex.getMessage());
        } catch (IOException ex) {
            ex.printStackTrace();
            fail("norrland: "+ex.getMessage());
        }
        
        // Chain: Kiruna Mining
        try {
            
            X509Certificate[] chain = new X509Certificate[] {
                new X509Certificate(getClass().getResourceAsStream(Files.KIRUNA_MINING_CA)),
                new X509Certificate(getClass().getResourceAsStream(Files.NORRLAND_CA)),
            };
            Certificate[] trustedCerts = new X509Certificate[]{new X509Certificate(getClass().getResourceAsStream(Files.SCANDINAVIA_ROOT_CA))};
            CertificateChain certChain = new X509CertificateChain(chain);
            
            assertTrue("kiruna", certChain.validateChain(trustedCerts));
            
        } catch (InvalidCertificateChain ex) {
            ex.printStackTrace();
            fail("kiruna: "+ex.getMessage());
        } catch (IOException ex) {
            ex.printStackTrace();
            fail("kiruna: "+ex.getMessage());
        }
        
        // Chain: Lars-Nils
        try {
            
            X509Certificate[] chain = new X509Certificate[] {
                new X509Certificate(getClass().getResourceAsStream(Files.LARS_NILS)),
                new X509Certificate(getClass().getResourceAsStream(Files.DALATRAFIK_CA)),
                new X509Certificate(getClass().getResourceAsStream(Files.DALARNA_CA)),
            };
            Certificate[] trustedCerts = new X509Certificate[]{new X509Certificate(getClass().getResourceAsStream(Files.NORDIC_ROOT_CA))};
            CertificateChain certChain = new X509CertificateChain(chain);
            
            assertTrue("larsNils", certChain.validateChain(trustedCerts));
            
        } catch (InvalidCertificateChain ex) {
            ex.printStackTrace();
            fail("larsNils: "+ex.getMessage());
        } catch (IOException ex) {
            ex.printStackTrace();
            fail("larsNils: "+ex.getMessage());
        }
        
    }
    
    private void testChainOrdered() throws AssertionFailedException {
        System.out.println("testChainOrdered");
        
        try {
            // Wrong order
            X509Certificate[] chain = new X509Certificate[]{new X509Certificate(getClass().getResourceAsStream("files/Other-TrainCompany-CA.cer"))};
            Certificate[] trustedCerts = new X509Certificate[]{new X509Certificate(getClass().getResourceAsStream("files/markus1-pub.cer"))};
            CertificateChain certChain = new X509CertificateChain(chain);
            
            assertFalse("wrong order 1", certChain.validateChain(trustedCerts));
            
        } catch (InvalidCertificateChain ex) {
            // OK
        }  catch (IOException ex) {
            ex.printStackTrace();
            fail(ex.getMessage());
        }
        
        try {
            // Wrong order
            X509Certificate[] chain = new X509Certificate[] { 
                new X509Certificate(getClass().getResourceAsStream("files/SigurdSigurdsson.cer")),
                new X509Certificate(getClass().getResourceAsStream("files/SodermanlandCA.cer")),
                new X509Certificate(getClass().getResourceAsStream("files/StockholmslanCA.cer")),
            };
            Certificate[] trustedCerts = new X509Certificate[]{new X509Certificate(getClass().getResourceAsStream("files/markus1-pub.cer"))};
            CertificateChain certChain = new X509CertificateChain(chain);
            
            assertFalse("wrong order 2", certChain.validateChain(trustedCerts));
            
        } catch (InvalidCertificateChain ex) {
            // OK
        }  catch (IOException ex) {
            ex.printStackTrace();
            fail(ex.getMessage());
        }
        
    }
    
    private void testWrongRootCertificate() throws AssertionFailedException {
        System.out.println("testWrongRootCertificate");
        
        // Chain: David Davidsson, wrong root 1
        try {
            
            X509Certificate[] chain = new X509Certificate[] {
                new X509Certificate(getClass().getResourceAsStream("files/DavidDavidsson.cer")),
                new X509Certificate(getClass().getResourceAsStream("files/TrainCompanyCA.cer")),
                new X509Certificate(getClass().getResourceAsStream("files/StockholmslanCA.cer")),
                new X509Certificate(getClass().getResourceAsStream("files/SodermanlandCA.cer"))
            };
            Certificate[] trustedCerts = new X509Certificate[]{new X509Certificate(getClass().getResourceAsStream("files/BusCompanyCA.cer"))};
            CertificateChain certChain = new X509CertificateChain(chain);
            
            assertFalse("David, wrong root 1", certChain.validateChain(trustedCerts));
            
        } catch (InvalidCertificateChain ex) {
            // OK
        } catch (IOException ex) {
            ex.printStackTrace();
            fail(ex.getMessage());
        }
        
        // Chain: David Davidsson, wrong root 2
        try {
            
            X509Certificate[] chain = new X509Certificate[] {
                new X509Certificate(getClass().getResourceAsStream("files/DavidDavidsson.cer")),
                new X509Certificate(getClass().getResourceAsStream("files/TrainCompanyCA.cer")),
                new X509Certificate(getClass().getResourceAsStream("files/StockholmslanCA.cer")),
                new X509Certificate(getClass().getResourceAsStream("files/SodermanlandCA.cer"))
            };
            Certificate[] trustedCerts = new X509Certificate[]{new X509Certificate(getClass().getResourceAsStream("files/SodermanlandCA.cer"))};
            CertificateChain certChain = new X509CertificateChain(chain);
            
            assertFalse("David, wrong root 2", certChain.validateChain(trustedCerts));
            
        } catch (InvalidCertificateChain ex) {
            // OK
        } catch (IOException ex) {
            ex.printStackTrace();
            fail(ex.getMessage());
        }
    }
    
    private void testWrongCertificateInChain() throws AssertionFailedException {
        System.out.println("testWrongRootCertificate");
        
        // Chain: David Davidsson, wrong chain 1
        try {
            
            X509Certificate[] chain = new X509Certificate[] {
                new X509Certificate(getClass().getResourceAsStream("files/DavidDavidsson.cer")),
                new X509Certificate(getClass().getResourceAsStream("files/BusCompanyCA.cer")),
                new X509Certificate(getClass().getResourceAsStream("files/StockholmslanCA.cer")),
                new X509Certificate(getClass().getResourceAsStream("files/SodermanlandCA.cer"))
            };
            Certificate[] trustedCerts = new X509Certificate[]{new X509Certificate(getClass().getResourceAsStream("files/SwedenRootCA.cer"))};
            CertificateChain certChain = new X509CertificateChain(chain);
            
            assertFalse("David, wrong chain 1", certChain.validateChain(trustedCerts));
            
        } catch (InvalidCertificateChain ex) {
            // OK
        } catch (IOException ex) {
            ex.printStackTrace();
            fail(ex.getMessage());
        }
        
        // Chain: David Davidsson, wrong chain 2
        try {
            
            X509Certificate[] chain = new X509Certificate[] {
                new X509Certificate(getClass().getResourceAsStream("files/DavidDavidsson.cer")),
                new X509Certificate(getClass().getResourceAsStream("files/SigurdSigurdsson.cer")),
                new X509Certificate(getClass().getResourceAsStream("files/StockholmslanCA.cer")),
                new X509Certificate(getClass().getResourceAsStream("files/SodermanlandCA.cer"))
            };
            Certificate[] trustedCerts = new X509Certificate[]{new X509Certificate(getClass().getResourceAsStream("files/SwedenRootCA.cer"))};
            CertificateChain certChain = new X509CertificateChain(chain);
            
            assertFalse("David, wrong chain 2", certChain.validateChain(trustedCerts));
            
        } catch (InvalidCertificateChain ex) {
            // OK
        } catch (IOException ex) {
            ex.printStackTrace();
            fail(ex.getMessage());
        }
        
        // Chain: David Davidsson, wrong chain 3, wrong TrainCompany CA
        try {
            
            X509Certificate[] chain = new X509Certificate[] {
                new X509Certificate(getClass().getResourceAsStream("files/DavidDavidsson.cer")),
                new X509Certificate(getClass().getResourceAsStream("files/false-TrainCompany-pub.cer")),
                new X509Certificate(getClass().getResourceAsStream("files/StockholmslanCA.cer")),
                new X509Certificate(getClass().getResourceAsStream("files/SodermanlandCA.cer"))
            };
            Certificate[] trustedCerts = new X509Certificate[]{new X509Certificate(getClass().getResourceAsStream("files/SwedenRootCA.cer"))};
            CertificateChain certChain = new X509CertificateChain(chain);
            
            assertFalse("David, wrong chain 3", certChain.validateChain(trustedCerts));
            
        } catch (InvalidCertificateChain ex) {
            // OK
        } catch (IOException ex) {
            ex.printStackTrace();
            fail(ex.getMessage());
        }
    }
    
    private void testToDate() throws AssertionFailedException {
        System.out.println("testToDate");
        
        // YYMMDDhhmmssGMT(+hh:mm|-hh:mm)
        
        Calendar cal = Calendar.getInstance();   
        
        // "20091001123813GMT+00:00"
        cal.set(Calendar.YEAR, 2009);
        cal.set(Calendar.MONTH, 10-1);
        cal.set(Calendar.DAY_OF_MONTH, 1);
        cal.set(Calendar.HOUR_OF_DAY, 12);
        cal.set(Calendar.MINUTE, 38);
        cal.set(Calendar.SECOND, 13);
        cal.set(Calendar.MILLISECOND, 0);
        assertEquals("20091001123813GMT+00:00", cal.getTime(), X509CertificateChain.toDate("20091001123813GMT+00:00"));
        
        // "20070523210408GMT+00:00"
        cal.set(Calendar.YEAR, 2007);
        cal.set(Calendar.MONTH, 5-1);
        cal.set(Calendar.DAY_OF_MONTH, 23);
        cal.set(Calendar.HOUR_OF_DAY, 21);
        cal.set(Calendar.MINUTE, 4);
        cal.set(Calendar.SECOND, 8);
        cal.set(Calendar.MILLISECOND, 0);
        assertEquals("20070523210408GMT+00:00", cal.getTime(), X509CertificateChain.toDate("20070523210408GMT+00:00"));
        
        // "20070523210408GMT+02:00"
        cal.set(Calendar.YEAR, 2007);
        cal.set(Calendar.MONTH, 5-1);
        cal.set(Calendar.DAY_OF_MONTH, 23);
        cal.set(Calendar.HOUR_OF_DAY, 19);
        cal.set(Calendar.MINUTE, 4);
        cal.set(Calendar.SECOND, 8);
        cal.set(Calendar.MILLISECOND, 0);
        assertEquals("20070523210408GMT+02:00", cal.getTime(), X509CertificateChain.toDate("20070523210408GMT+02:00"));
        
        // "20070523210408GMT-11:00"
        cal.set(Calendar.YEAR, 2007);
        cal.set(Calendar.MONTH, 5-1);
        cal.set(Calendar.DAY_OF_MONTH, 24);
        cal.set(Calendar.HOUR_OF_DAY, 8);
        cal.set(Calendar.MINUTE, 4);
        cal.set(Calendar.SECOND, 8);
        cal.set(Calendar.MILLISECOND, 0);
        
        assertEquals("20070523210408GMT-11:00", cal.getTime(), X509CertificateChain.toDate("20070523210408GMT-11:00"));
        
        // "20170523210408GMT+02:45"
        cal.set(Calendar.YEAR, 2017);
        cal.set(Calendar.MONTH, 5-1);
        cal.set(Calendar.DAY_OF_MONTH, 23);
        cal.set(Calendar.HOUR_OF_DAY, 18);
        cal.set(Calendar.MINUTE, 19);
        cal.set(Calendar.SECOND, 8);
        cal.set(Calendar.MILLISECOND, 0);
        assertEquals("20170523210408GMT+02:45", cal.getTime(), X509CertificateChain.toDate("20170523210408GMT+02:45"));
    }
    
    private void testValidDate() throws AssertionFailedException {
        System.out.println("testValidDate");
        
        // Chain: David Davidsson, valid times
        try {
            
            X509Certificate[] chain = new X509Certificate[] {
                new X509Certificate(getClass().getResourceAsStream("files/DavidDavidsson.cer")),
                new X509Certificate(getClass().getResourceAsStream("files/TrainCompanyCA.cer")),
                new X509Certificate(getClass().getResourceAsStream("files/StockholmslanCA.cer")),
                new X509Certificate(getClass().getResourceAsStream("files/SodermanlandCA.cer"))
            };
            Certificate[] trustedCerts = new X509Certificate[]{new X509Certificate(getClass().getResourceAsStream("files/SwedenRootCA.cer"))};
            CertificateChain certChain = new X509CertificateChain(chain);
            
            assertTrue("David, valid times", certChain.validateChain(trustedCerts));
            
        } catch (InvalidCertificateChain ex) {
            ex.printStackTrace();
            fail(ex.getMessage());
        } catch (IOException ex) {
            ex.printStackTrace();
            fail(ex.getMessage());
        }
        
        // Chain: Sigurd Sigurdsson, valid times
        try {
            
            X509Certificate[] chain = new X509Certificate[] {
                new X509Certificate(getClass().getResourceAsStream("files/SigurdSigurdsson.cer")),
                new X509Certificate(getClass().getResourceAsStream("files/StockholmslanCA.cer")),
                new X509Certificate(getClass().getResourceAsStream("files/SodermanlandCA.cer"))
            };
            Certificate[] trustedCerts = new X509Certificate[] {new X509Certificate(getClass().getResourceAsStream("files/SwedenRootCA.cer"))};
            CertificateChain certChain = new X509CertificateChain(chain);
            
            assertTrue("Sigurd, valid times", certChain.validateChain(trustedCerts));
            
        } catch (InvalidCertificateChain ex) {
            ex.printStackTrace();
            fail(ex.getMessage());
        } catch (IOException ex) {
            ex.printStackTrace();
            fail(ex.getMessage());
        }
        
        // Chain: Gustaf Gustafsson, expired Feb 2008
        try {
            Certificate[] trustedCerts = new X509Certificate[]{new X509Certificate(getClass().getResourceAsStream("files/old-gustafssonsfood-pub.cer"))};
//            System.out.println("root end: " + rootCertificate.getStructure().getEndDate().getTime());
            CertificateChain certChain = new X509CertificateChain(new X509Certificate[]{});
            
            assertFalse("Gustaf, expired", certChain.validateChain(trustedCerts));
            
        } catch (InvalidCertificateChain ex) {} // OK
        catch (IOException ex) {
            ex.printStackTrace();
            fail(ex.getMessage());
        }
        
        // Chain: Erik Eriksson, expired 2008-10-02 14:38
        try {
            
            X509Certificate[] chain = new X509Certificate[] {
                new X509Certificate(getClass().getResourceAsStream("files/old-ErikEriksson.cer")),
                new X509Certificate(getClass().getResourceAsStream("files/TrainCompanyCA.cer")),
                new X509Certificate(getClass().getResourceAsStream("files/StockholmslanCA.cer")),
                new X509Certificate(getClass().getResourceAsStream("files/SodermanlandCA.cer"))
            };
            Certificate[] trustedCerts = new X509Certificate[]{new X509Certificate(getClass().getResourceAsStream("files/SwedenRootCA.cer"))};
            CertificateChain certChain = new X509CertificateChain(chain);
            
            assertFalse("Erik, expired", certChain.validateChain(trustedCerts));
            
        } catch (InvalidCertificateChain ex) {} // OK
        catch (IOException ex) {
            ex.printStackTrace();
            fail(ex.getMessage());
        }
        
        // Chain: Harald Haraldsson, not before 2010-10-06 15:29:12
        try {
            
            X509Certificate[] chain = new X509Certificate[] {};
            Certificate[] trustedCerts = new X509Certificate[]{new X509Certificate(getClass().getResourceAsStream("files/new-harald.cer"))};
            CertificateChain certChain = new X509CertificateChain(chain);
            
            assertFalse("Harald, not before 2010-10-06 15:29:12", certChain.validateChain(trustedCerts));
            
        } catch (InvalidCertificateChain ex) {} // OK
        catch (IOException ex) {
            ex.printStackTrace();
            fail(ex.getMessage());
        }
        
    }

    private void testRsaSsaPkcs1V15SatsaVerifier() throws AssertionFailedException {
        System.out.println("testRsaSsaPkcs1V15SatsaVerifier");

        if(!implDetector.isSatsaAvailable()) {
            System.out.println("Test skipped: No Support for SATSA");
            return;
        }
        
        SignatureVerifier verifier = RsaSsaPkcs1V15SatsaVerifier.getInstance();
        testWithSignaturesFromCertificates("PKCS1-SATSA", verifier);
    }
    
    private void testRsaSsaPkcs1v15BcVerifier() throws AssertionFailedException {
        System.out.println("testRsaSsaPkcs1v15BcVerifier");

        SignatureVerifier verifier = RsaSsaPkcs1v15BcVerifier.getInstance();
        testWithSignaturesFromCertificates("PKCS1-BC", verifier);
    }
    
    private void testDSASatsaVerifier() throws AssertionFailedException {
        System.out.println("testDSASatsaVerifier");

        if(!implDetector.isSatsaAvailable()) {
            System.out.println("Test skipped: No Support for SATSA");
            return;
        }
        
        SignatureVerifier verifier = DSASatsaVerifier.getInstance();
        
        if(verifier == null) {
            System.out.println("Test skipped: No support for DSA in SATSA");
            return;
        }
        
        testWithSignaturesFromCertificates("DSA-SATSA", verifier);
    }
    
    private void testWithSignaturesFromCertificates(String title, SignatureVerifier verifier) throws AssertionFailedException {
        try {

            X509Certificate certOldErik = new X509Certificate(getClass().getResourceAsStream("files/old-ErikEriksson.cer"));
            X509Certificate certTrainCompany = new X509Certificate(getClass().getResourceAsStream("files/TrainCompanyCA.cer"));
            X509Certificate certStockholm = new X509Certificate(getClass().getResourceAsStream("files/StockholmslanCA.cer"));
            X509Certificate certSodermanland = new X509Certificate(getClass().getResourceAsStream("files/SodermanlandCA.cer"));
            X509Certificate certSweden = new X509Certificate(getClass().getResourceAsStream("files/SwedenRootCA.cer"));

            X509Certificate certOldErikIssuer = certTrainCompany;
            X509Certificate certTrainCompanyIssuer = certStockholm;
            X509Certificate certStockholmIssuer = certSodermanland;
            X509Certificate certSodermanlandIssuer = certSweden;
            X509Certificate certSwedenIssuer = certSweden;
            
            X509Certificate sthlmToBeModified = new X509Certificate(getClass().getResourceAsStream("files/StockholmslanCA.cer"));
            byte[] sthlmModifiedBytes = sthlmToBeModified.getBytes();
            sthlmModifiedBytes[280] = 106;
            X509Certificate certSthlmModified = new X509Certificate(sthlmModifiedBytes);
            
            assertTrue(title + ": sig 1", verifySignatureOfCertificate(verifier, certOldErik, certOldErikIssuer));
            assertTrue(title + ": sig 2", verifySignatureOfCertificate(verifier, certTrainCompany, certTrainCompanyIssuer));
            assertTrue(title + ": sig 3", verifySignatureOfCertificate(verifier, certStockholm, certStockholmIssuer));
            assertTrue(title + ": sig 4", verifySignatureOfCertificate(verifier, certSodermanland, certSodermanlandIssuer));
            assertTrue(title + ": sig 5", verifySignatureOfCertificate(verifier, certSweden, certSwedenIssuer));
            
            assertFalse(title + ": sig 6 modified", verifySignatureOfCertificate(verifier, certSthlmModified, certStockholmIssuer));
            
            assertFalse(title + ": sig 7 wrong key", verifySignatureOfCertificate(verifier, certOldErik, certSwedenIssuer));
            assertFalse(title + ": sig 8 wrong key", verifySignatureOfCertificate(verifier, certTrainCompany, certOldErikIssuer));
            assertFalse(title + ": sig 9 wrong key", verifySignatureOfCertificate(verifier, certStockholm, certSodermanlandIssuer));
            assertFalse(title + ": sig 10 wrong key", verifySignatureOfCertificate(verifier, certSodermanland, certStockholmIssuer));
            assertFalse(title + ": sig 11 wrong key", verifySignatureOfCertificate(verifier, certSweden, certTrainCompanyIssuer));
            
        } catch (SignatureException ex) {
            ex.printStackTrace();
            fail("SignatureException: " + ex.getMessage());
        } catch (IOException ex) {
            ex.printStackTrace();
            fail("IOException: " + ex.getMessage());
        }
    }

    private boolean verifySignatureOfCertificate(SignatureVerifier verifier, X509Certificate cert, X509Certificate issuer) throws SignatureException {        
        return verifier.verifySignature(cert.getStructure().getSignature().getBytes(), cert.getStructure().getTBSCertificate().getDEREncoded(), new PublicKey(issuer.getSubjectPublicKeyInfo()));
    }

    private void testPKCS1Signer()  throws AssertionFailedException{
        try {
            System.out.println("testPKCS1Signer");
            
            signCertificateTest("markus4", new X509Certificate(getClass().getResourceAsStream("files/markus4-pub.cer")), new PrivateKey(getClass().getResourceAsStream("files/markus4-priv.der")));
            
        } catch (CryptoException ex) {
            ex.printStackTrace();
            fail("CryptoException: " + ex.getMessage());
        } catch (DataLengthException ex) {
            ex.printStackTrace();
            fail("DataLengthException: " + ex.getMessage());
        } catch (IOException ex) {
            ex.printStackTrace();
            fail("IOException: " + ex.getMessage());
        }
    }
    
    private void signCertificateTest(String title, X509Certificate cert, PrivateKey key)  throws AssertionFailedException, CryptoException, IOException {
        
        byte[] data = cert.getStructure().getTBSCertificate().getEncoded();
        
        RSAPrivateKeyStructure structure = RSAPrivateKeyStructure.getInstance(key.getPrivateKeyInfo().getPrivateKey());
        RSAKeyParameters param = new RSAKeyParameters(true, structure.getModulus(), structure.getPrivateExponent());

        PKCS1Signer signer = new PKCS1Signer(new RSAEngine(), new SHA1Digest());
        signer.init(true, param);
        signer.update(data, 0, data.length);

        byte[] signature = signer.generateSignature();

        System.out.println("Old sig:" + Utils.toFixedHexString(cert.getStructure().getSignature().getBytes()));
        System.out.println("New sig:" + Utils.toFixedHexString(signature));

        assertTrue(title, Utils.equals(cert.getStructure().getSignature().getBytes(), signature));
    }
    
    private void testFindMatchingRootCertificate() throws AssertionFailedException {
        try {
            System.out.println("testFindMatchingRootCertificate");

            X509Certificate swedenRootCA = new X509Certificate(getClass().getResourceAsStream("files/SwedenRootCA.cer"));
            
            X509Certificate[] trusted1 = new X509Certificate[] { 
                new X509Certificate(getClass().getResourceAsStream("files/BertilBertilsson.cer")), 
                new X509Certificate(getClass().getResourceAsStream("files/markus1-pub.cer")),
                swedenRootCA,
            };
            
            X509Certificate adam = new X509Certificate(getClass().getResourceAsStream("files/AdamAdamsson.cer"));
            X509Certificate bus = new X509Certificate(getClass().getResourceAsStream("files/BusCompanyCA.cer"));
            X509Certificate stockholm = new X509Certificate(getClass().getResourceAsStream("files/StockholmslanCA.cer"));
            X509Certificate sodermanland = new X509Certificate(getClass().getResourceAsStream("files/SodermanlandCA.cer"));
            
            X509Certificate[] chain1 = new X509Certificate[] { 
                adam, 
                bus,
                stockholm, 
                sodermanland, 
            };
            
            for(int i = 0; i < trusted1.length; i++) {
                System.out.println("trusted: "+trusted1[i].getStructure().getSubject());    
            }
            
            for(int i = 0; i < chain1.length; i++) {
                System.out.println("chain issued by: "+chain1[i].getStructure().getIssuer());    
            }
            
            assertEquals(swedenRootCA, X509CertificateChain.findMatchingRootCertificate(chain1, trusted1));
            
        } catch (IOException ex) {
            ex.printStackTrace();
            fail(ex.getMessage());
        }
    }

    private void testValidateChainNoKeyUsageSign() throws AssertionFailedException {
        System.out.println("testValidateChainNoKeyUsageSign");

        // Chain: User with no sign right
        try {

            X509Certificate[] chain = new X509Certificate[] {
                new X509Certificate(getClass().getResourceAsStream(Files.USER_NO_SIGN_RIGHT)),
                new X509Certificate(getClass().getResourceAsStream(Files.KIRUNA_MINING_CA)),
                new X509Certificate(getClass().getResourceAsStream(Files.NORRLAND_CA))
            };
            Certificate[] trustedCerts = new X509Certificate[]{new X509Certificate(getClass().getResourceAsStream(Files.SCANDINAVIA_ROOT_CA))};
            CertificateChain certChain = new X509CertificateChain(chain);

            assertFalse("UserWithNoSignRight", certChain.validateChain(trustedCerts));

        } catch (InvalidCertificateChain ex) {
            ex.printStackTrace();
            fail("UserWithNoSignRight: "+ex.getMessage());
        } catch (IOException ex) {
            ex.printStackTrace();
            fail("UserWithNoSignRight: "+ex.getMessage());
        }

    }

    private void testValidateChainNoCA() throws AssertionFailedException {
        System.out.println("testValidateChainNoCA");

        // Chain: False user 1
        try {

            X509Certificate[] chain = new X509Certificate[] {
                new X509Certificate(getClass().getResourceAsStream(Files.FALSE_USER_1)),
                new X509Certificate(getClass().getResourceAsStream(Files.FALSE_CA)),
                new X509Certificate(getClass().getResourceAsStream(Files.KIRUNA_MINING_CA)),
                new X509Certificate(getClass().getResourceAsStream(Files.NORRLAND_CA))
            };
            Certificate[] trustedCerts = new X509Certificate[]{new X509Certificate(getClass().getResourceAsStream(Files.SCANDINAVIA_ROOT_CA))};
            CertificateChain certChain = new X509CertificateChain(chain);

            assertFalse("False CA not a CA", certChain.validateChain(trustedCerts));
            System.out.println("msg="+certChain.getValidationMessage());

        } catch (InvalidCertificateChain ex) {
            ex.printStackTrace();
            fail("False CA not a CA: "+ex.getMessage());
        } catch (IOException ex) {
            ex.printStackTrace();
            fail("False CA not a CA: "+ex.getMessage());
        }

    }

    private void testValidateChainCriticalExtension() throws AssertionFailedException {
        System.out.println("testValidateChainCriticalExtension");

        // Chain: User CritExt1
        try {

            X509Certificate[] chain = new X509Certificate[] {
                new X509Certificate(getClass().getResourceAsStream(Files.USER_CRITICAL_EXTENSION_1)),
                new X509Certificate(getClass().getResourceAsStream(Files.KIRUNA_MINING_CA)),
                new X509Certificate(getClass().getResourceAsStream(Files.NORRLAND_CA))
            };
            Certificate[] trustedCerts = new X509Certificate[]{new X509Certificate(getClass().getResourceAsStream(Files.SCANDINAVIA_ROOT_CA))};
            CertificateChain certChain = new X509CertificateChain(chain);

            assertFalse("User CritExt1", certChain.validateChain(trustedCerts));
            System.out.println("msg="+certChain.getValidationMessage());

        } catch (InvalidCertificateChain ex) {
            ex.printStackTrace();
            fail("User CritExt1: "+ex.getMessage());
        } catch (IOException ex) {
            ex.printStackTrace();
            fail("User CritExt1: "+ex.getMessage());
        }

    }

    private void testValidateChainPathLen() throws AssertionFailedException {
        System.out.println("testValidateChainPathLen");

        // Chain: Silver CA
        try {

            X509Certificate[] chain = new X509Certificate[] {
                new X509Certificate(getClass().getResourceAsStream(Files.SILVER_CA)),
                new X509Certificate(getClass().getResourceAsStream(Files.GOLD_CA)),
                new X509Certificate(getClass().getResourceAsStream(Files.KIRUNA_MINING_CA)),
                new X509Certificate(getClass().getResourceAsStream(Files.NORRLAND_CA))
            };
            Certificate[] trustedCerts = new X509Certificate[]{new X509Certificate(getClass().getResourceAsStream(Files.SCANDINAVIA_ROOT_CA))};
            CertificateChain certChain = new X509CertificateChain(chain);

            assertFalse("Path len", certChain.validateChain(trustedCerts));
            System.out.println("msg="+certChain.getValidationMessage());

        } catch (InvalidCertificateChain ex) {
            ex.printStackTrace();
            fail("Path len: "+ex.getMessage());
        } catch (IOException ex) {
            ex.printStackTrace();
            fail("Path len: "+ex.getMessage());
        }
    }

    private void testValidateChainDSAParamsInherited() throws AssertionFailedException {
        System.out.println("testValidateChainDSAParamsInherited");

        // Chain: Jan-Erik
        try {

            X509Certificate[] chain = new X509Certificate[] {
                new X509Certificate(getClass().getResourceAsStream(Files.JAN_ERIK)),
                new X509Certificate(getClass().getResourceAsStream(Files.NORRBOTTEN_CA))
            };
            Certificate[] trustedCerts = new X509Certificate[]{new X509Certificate(getClass().getResourceAsStream(Files.SCANDINAVIA_ROOT_CA))};
            CertificateChain certChain = new X509CertificateChain(chain);

            boolean success = certChain.validateChain(trustedCerts);
            System.out.println("msg=" + certChain.getValidationMessage());
            assertTrue("Jan-Erik", success);

        } catch (InvalidCertificateChain ex) {
            ex.printStackTrace();
            fail("Jan-Erik: "+ex.getMessage());
        } catch (IOException ex) {
            ex.printStackTrace();
            fail("Jan-Erik: "+ex.getMessage());
        }
    }

    private void testValidateCertWithRSASSAPSS() throws AssertionFailedException {

        // Chain: B user 2 (RSASSA-PSS)
        try {

            X509Certificate[] chain = new X509Certificate[] {
                new X509Certificate(getClass().getResourceAsStream(Files.B_USER2))
            };
            Certificate[] trustedCerts = new X509Certificate[]{new X509Certificate(getClass().getResourceAsStream(Files.B_ROOT_CA))};
            CertificateChain certChain = new X509CertificateChain(chain);

            assertTrue("Should have consistent signature, but:"+certChain.getValidationMessage(), certChain.validateChain(trustedCerts));

        } catch (InvalidCertificateChain ex) {
            ex.printStackTrace();
            fail("InvalidCertificateChain: "+ex.getMessage());
        } catch (IOException ex) {
            ex.printStackTrace();
            fail("IOException: "+ex.getMessage());
        }
    }
}
